#include "BasicMesh.h"

//--------------------------------------------------------------------------------------------
BasicMesh::BasicMesh()							// Default constructor.
	: m_material		(0),
	  m_diffuse_map		(0), 
	  m_enable_texture	(false)
{
	XMMATRIX I = XMMatrixIdentity();

	// Set the mesh translation and rotation to zero, scale to 1.
	m_translation	= XMFLOAT3(0.0f, 0.0f, 0.0f);
	m_rotation		= XMFLOAT3(0.0f, 0.0f, 0.0f);
	m_scale			= XMFLOAT3(1.0f, 1.0f, 1.0f);

	// Set the texture transform matrix to the identity matrix.
	XMStoreFloat4x4(&m_tex_transform, I);

	// Get a pointer to the single basic shader instance to render the mesh.
	m_BasicShader = BasicShader::GetInstance();
}

//--------------------------------------------------------------------------------------------
BasicMesh::BasicMesh(								// Custom constructor.
	ID3D11Buffer*				vb,					// Pointer to mesh vertex buffer.
	ID3D11Buffer*				ib,					// Pointer to mesh index buffer.
	Material*					mat,				// Pointer to mesh material.
	ID3D11ShaderResourceView*	dmap,				// Pointer to mesh diffuse texture.
	bool						texEnable)			// Flag to enable or disable drawing the diffuse texture.
	:				
	  m_vertex_buffer	(vb),
	  m_index_buffer	(ib),
	  m_material		(mat),
	  m_diffuse_map		(dmap),
	  m_enable_texture	(texEnable)
{
	XMMATRIX I		= XMMatrixIdentity();
	
	// Set the mesh translation and rotation to zero, scale to 1.
	m_translation	= XMFLOAT3(0.0f, 0.0f, 0.0f);
	m_rotation		= XMFLOAT3(0.0f, 0.0f, 0.0f);
	m_scale			= XMFLOAT3(1.0f, 1.0f, 1.0f);

	// Set the texture transform matrix to the identity matrix.
	XMStoreFloat4x4(&m_tex_transform, I);
	
	// Calculate the index count of this mesh.
	D3D11_BUFFER_DESC ibd;
	m_index_buffer	->GetDesc(&ibd);
	m_index_count	= (ibd.ByteWidth) / sizeof(UINT);

	// Get a pointer to the single basic shader instance to render the mesh.
	m_BasicShader	= BasicShader::GetInstance();
}

//--------------------------------------------------------------------------------------------
BasicMesh::BasicMesh(								// Custom contructor.
	ID3D11Buffer*				vb,					// Pointer to mesh vertex buffer.
	ID3D11Buffer*				ib,					// Pointer to mesh index buffer.
	Material*					mat,				// Pointer to mesh material.
	ID3D11ShaderResourceView*	dmap,				// Pointer to mesh diffuse texture.
	XMFLOAT3&					tran,				// Translation vector.
	XMFLOAT3&					rot,				// Rotation vector.
	XMFLOAT3&					scale,				// Scale vector
	XMFLOAT3&					text,				// Pointer to mesh diffuse texture.
	bool						texEnable)			// Flag to enable or disable drawing the diffuse texture.
	: m_vertex_buffer	(vb), 
	  m_index_buffer	(ib), 
	  m_material		(mat), 
	  m_diffuse_map		(dmap), 
	  m_enable_texture	(texEnable)
{
	// Initialise the mesh transformation data to corresponding passed in data.
	SetTranslation	(tran.x, tran.y, tran.z);
	SetRotation		(rot.x, rot.y, rot.z);
	SetScale		(scale.x, scale.y, scale.z);
	SetTexTransform	(text.x, text.y, text.z);

	// Calculate the index count of this mesh.
	D3D11_BUFFER_DESC ibd;
	m_index_buffer	->GetDesc(&ibd);
	m_index_count	= (ibd.ByteWidth) / sizeof(UINT);

	// Get a pointer to the single basic shader instance to render the mesh.
	m_BasicShader	= BasicShader::GetInstance();
}

//--------------------------------------------------------------------------------------------
BasicMesh::~BasicMesh()
{
	ReleaseCOM(m_diffuse_map);
	ReleaseCOM(m_vertex_buffer);
	ReleaseCOM(m_index_buffer);
	
	m_material		= nullptr;
	m_BasicShader	= nullptr;
}

//--------------------------------------------------------------------------------------------
void BasicMesh::Draw(ID3D11DeviceContext* device_context, XMMATRIX& view, XMMATRIX& proj)
{
	UINT stride = sizeof(BasicShader::Vertex);
	UINT offset = 0;
	
	// Set up technique to use in FX.
	ID3DX11EffectTechnique* active_tech;

	if (m_enable_texture == false)
		active_tech = m_BasicShader->Light3Tech;
	else
		active_tech = m_BasicShader->Light3TexTech;

	// Get description of technique to access number of passes.
	D3DX11_TECHNIQUE_DESC tech_desc;
	active_tech->GetDesc(&tech_desc);

	for (UINT p=0; p < tech_desc.Passes; ++p)
	{
		// Set vertex and index buffer.
		device_context->IASetVertexBuffers(0, 1, &m_vertex_buffer, &stride, &offset);
		device_context->IASetIndexBuffer(m_index_buffer, DXGI_FORMAT_R32_UINT, 0);

		// Send object constants to FX file.
		XMMATRIX R = XMMatrixRotationRollPitchYaw(m_rotation.x, m_rotation.y, m_rotation.z);
		XMMATRIX S = XMMatrixScaling(m_scale.x, m_scale.y, m_scale.z);
		XMMATRIX T = XMMatrixTranslation(m_translation.x, m_translation.y, m_translation.z);
		
		XMMATRIX world = R*S*T;
		XMMATRIX worldInvTrans = MathHelper::InverseTranspose(world);
		XMMATRIX worldViewProj = world * view * proj;
		XMMATRIX texTransform = XMLoadFloat4x4(&m_tex_transform);

		m_BasicShader->SetWorld(world);
		m_BasicShader->SetWorldInvTranspose(worldInvTrans);
		m_BasicShader->SetWorldViewProj(worldViewProj);
		m_BasicShader->SetTexTransform(texTransform);
		m_BasicShader->SetMaterial(*m_material);
		m_BasicShader->SetDiffuseMap(m_diffuse_map);

		// Apply constants and draw primitive.
		active_tech->GetPassByIndex(p)->Apply(0, device_context);
		device_context->DrawIndexed(m_index_count, 0, 0);
	}
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetTranslation(const float tx, const float ty, const float tz)
{
	// Set mesh world position.
	m_translation.x = tx;
	m_translation.y = ty;
	m_translation.z = tz;
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetRotation(const float rx, const float ry, const float rz)
{	
	// Set mesh world rotation (degrees).
	m_rotation.x = rx * (XM_PI / 180);
	m_rotation.y = ry * (XM_PI / 180);
	m_rotation.z = rz * (XM_PI / 180);
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetScale(const float sx, const float sy, const float sz)
{
	// Set mesh scale data.
	m_scale.x = sx;
	m_scale.y = sy;
	m_scale.z = sz;
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetTexTransform(const float stx, const float sty, const float stz)
{
	// Set mesh texture transform matrix.
	CXMMATRIX S = XMMatrixScaling(stx, sty, stz);

	XMStoreFloat4x4(&m_tex_transform, S);
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetMaterial(Material* mat)
{
	// Set mesh material.
	m_material = mat;
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetDiffuseMap(ID3D11ShaderResourceView* diffuse_map)
{
	// Set mesh diffuse texture.
	m_diffuse_map = diffuse_map;
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetVertexBuffer(ID3D11Buffer* vb)
{
	// Update mesh veretx buffer pointer.
	m_vertex_buffer = vb;
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetIndexBuffer(ID3D11Buffer* ib)
{
	// Update mesh index buffer pointer.
	m_index_buffer = ib;

	// Update mesh index count.
	D3D11_BUFFER_DESC ibd;
	m_index_buffer	->GetDesc(&ibd);
	m_index_count	= (ibd.ByteWidth) / sizeof(UINT);
}

//--------------------------------------------------------------------------------------------
XMFLOAT3 BasicMesh::GetTranslation() const
{
	// Return mesh world position.
	return m_translation;
}

void BasicMesh::EnableTexture(bool enable)
{
	// Set texture draw flag.
	m_enable_texture = enable;
}

//--------------------------------------------------------------------------------------------
void BasicMesh::Update(float dt)
{
	// TODO: Implement with compoenents.
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetAABB(XNA::AxisAlignedBox* aabb)
{ }

//--------------------------------------------------------------------------------------------
XNA::AxisAlignedBox* BasicMesh::GetAABB() const
{
	return NULL;
}

//--------------------------------------------------------------------------------------------
string& BasicMesh::GetID()
{
	// Return mesh ID (for hardware instancing).
	return m_id;
}

//--------------------------------------------------------------------------------------------
void BasicMesh::SetID(const string& id)
{
	// Set the mesh ID (for hardware instancing).
	m_id = id;
}